﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows;
using Eniux.Windows.ThreeDEngine.Helper;
using System.Windows.Media;

namespace Eniux.Windows.ThreeDEngine.Visual3Ds
{
    /// <summary>
    /// 用于显示网格的三维模型
    /// </summary>
    public class GridLinesVisual3D : ModelVisual3D
    {
        #region 注册属性

        #region Center
        /// <summary>
        /// 中心点属性
        /// </summary>
        public static readonly DependencyProperty CenterProperty =
        DependencyProperty.Register("Center", typeof(Point3D), typeof(GridLinesVisual3D),
                                new UIPropertyMetadata(new Point3D(), GeometryChanged));

        /// <summary>
        /// 获取或设置中心点
        /// </summary>
        public Point3D Center
        {
            get { return (Point3D)GetValue(CenterProperty); }
            set { SetValue(CenterProperty, value); }
        } 
        #endregion

        #region MajorDistance

        /// <summary>
        /// 主距离属性
        /// </summary>
        public static readonly DependencyProperty MajorDistanceProperty =
        DependencyProperty.Register("MajorDistance", typeof(double), typeof(GridLinesVisual3D),
                                new PropertyMetadata(10.0, GeometryChanged));

        /// <summary>
        /// 获取或设置主距离
        /// </summary>
        public double MajorDistance
        {
            get { return (double)GetValue(MajorDistanceProperty); }
            set { SetValue(MajorDistanceProperty, value); }
        }
        #endregion

        #region MajorLineThickness

        /// <summary>
        /// 主线粗细属性
        /// </summary>
        public static readonly DependencyProperty MajorLineThicknessProperty =
        DependencyProperty.Register("MajorLineThickness", typeof(double), typeof(GridLinesVisual3D), new UIPropertyMetadata(1.2));

        public double MajorLineThickness
        {
            get { return (double)GetValue(MajorLineThicknessProperty); }
            set { SetValue(MajorLineThicknessProperty, value); }
        }

        #endregion

        #region MajorLineColor

        /// <summary>
        /// 主线颜色属性
        /// </summary>
        public static readonly DependencyProperty MajorLineColorProperty =
            DependencyProperty.Register("MajorLineColor", typeof(Color), typeof(GridLinesVisual3D), new UIPropertyMetadata(Color.FromRgb(140, 140, 140), GeometryChanged));

        /// <summary>
        /// 获取或设置主线颜色
        /// </summary>
        public Color MajorLineColor
        {
            get { return (Color)GetValue(MajorLineColorProperty); }
            set { SetValue(MajorLineColorProperty, value); }
        }
        #endregion

        #region MinorDistance

        /// <summary>
        /// 次距离属性
        /// </summary>
        public static readonly DependencyProperty DistanceProperty =
        DependencyProperty.Register("MinorDistance", typeof(double), typeof(GridLinesVisual3D),
                                new PropertyMetadata(1.0, GeometryChanged));
        /// <summary>
        /// 获取或设置此距离
        /// </summary>
        public double MinorDistance
        {
            get { return (double)GetValue(DistanceProperty); }
            set { SetValue(DistanceProperty, value); }
        }
        #endregion

        #region MinorLineThickness

        /// <summary>
        /// 次线粗细属性
        /// </summary>
        public static readonly DependencyProperty MinorLineThicknessProperty =
        DependencyProperty.Register("MinorLineThickness", typeof(double), typeof(GridLinesVisual3D), new UIPropertyMetadata(1.0));

        /// <summary>
        /// 获取或设置次线粗细
        /// </summary>
        public double MinorLineThickness
        {
            get { return (double)GetValue(MinorLineThicknessProperty); }
            set { SetValue(MinorLineThicknessProperty, value); }
        }

        #endregion

        #region MinorLineColor

        /// <summary>
        /// 次线颜色属性
        /// </summary>
        public static readonly DependencyProperty MinorLineColorProperty =
        DependencyProperty.Register("MinorLineColor", typeof(Color), typeof(GridLinesVisual3D), new UIPropertyMetadata(Color.FromRgb(150, 150, 150), GeometryChanged));

        /// <summary>
        /// 获取或设置次线颜色
        /// </summary>
        public Color MinorLineColor
        {
            get { return (Color)GetValue(MinorLineColorProperty); }
            set { SetValue(MinorLineColorProperty, value); }
        }
        #endregion

        #region Length

        /// <summary>
        /// 长度属性
        /// </summary>
        public static readonly DependencyProperty LengthProperty =
        DependencyProperty.Register("Length", typeof(double), typeof(GridLinesVisual3D),
                                new PropertyMetadata(100.0, GeometryChanged));
        /// <summary>
        ///  获取或设置长度
        /// </summary>
        public double Length
        {
            get { return (double)GetValue(LengthProperty); }
            set { SetValue(LengthProperty, value); }
        } 
        #endregion

        #region Width
        /// <summary>
        /// 宽度属性
        /// </summary>
        public static readonly DependencyProperty WidthProperty =
            DependencyProperty.Register("Width", typeof(double), typeof(GridLinesVisual3D),
                                        new PropertyMetadata(100.0, GeometryChanged));
        /// <summary>
        /// 获取或设置宽度
        /// </summary>
        public double Width
        {
            get { return (double)GetValue(WidthProperty); }
            set { SetValue(WidthProperty, value); }
        }
        #endregion

        #region LengthDirection

        /// <summary>
        /// 长度方向属性
        /// </summary>
        public static readonly DependencyProperty LengthDirectionProperty =
            DependencyProperty.Register("LengthDirection", typeof(Vector3D), typeof(GridLinesVisual3D),
                                        new UIPropertyMetadata(new Vector3D(1, 0, 0), GeometryChanged));
        /// <summary>
        /// 获取或设置长度方向
        /// </summary>
        public Vector3D LengthDirection
        {
            get { return (Vector3D)GetValue(LengthDirectionProperty); }
            set { SetValue(LengthDirectionProperty, value); }
        } 
        #endregion

        #region Normal

        /// <summary>
        /// 法线属性
        /// </summary>
        public static readonly DependencyProperty NormalProperty =
        DependencyProperty.Register("Normal", typeof(Vector3D), typeof(GridLinesVisual3D),
                                new UIPropertyMetadata(new Vector3D(0, 0, 1), GeometryChanged));

        /// <summary>
        /// 获取或设置法线
        /// </summary>
        public Vector3D Normal
        {
            get { return (Vector3D)GetValue(NormalProperty); }
            set { SetValue(NormalProperty, value); }
        } 
        #endregion

        #region XAxisLineColor

        /// <summary>
        /// X轴线颜色属性
        /// </summary>
        public static readonly DependencyProperty XAxisLineColorProperty =
            DependencyProperty.Register("XAxisLineColor", typeof(Color), typeof(GridLinesVisual3D), new UIPropertyMetadata(Color.FromRgb(150, 75, 75)));

        /// <summary>
        /// 获取或设置X轴线颜色
        /// </summary>
        public Color XAxisLineColor
        {
            get { return (Color)GetValue(XAxisLineColorProperty); }
            set { SetValue(XAxisLineColorProperty, value); }
        } 

        #endregion

        #region YAxisLineColor

        /// <summary>
        /// Y轴线颜色属性
        /// </summary>
        public static readonly DependencyProperty YAxisLineColorProperty =
        DependencyProperty.Register("YAxisLineColor", typeof(Color), typeof(GridLinesVisual3D), new UIPropertyMetadata(Color.FromRgb(75, 150, 75)));

        /// <summary>
        /// 获取或设置Y轴线颜色
        /// </summary>
        public Color YAxisLineColor
        {
            get { return (Color)GetValue(YAxisLineColorProperty); }
            set { SetValue(YAxisLineColorProperty, value); }
        } 

        #endregion

        #region ZAxisLineColor

        /// <summary>
        /// Z轴线颜色属性
        /// </summary>
        public static readonly DependencyProperty ZAxisLineColorProperty =
        DependencyProperty.Register("ZAxisLineColor", typeof(Color), typeof(GridLinesVisual3D), new UIPropertyMetadata(Color.FromRgb(75, 75, 150)));

        /// <summary>
        /// 获取或设置Z轴线颜色
        /// </summary>
        public Color ZAxisLineColor
        {
            get { return (Color)GetValue(ZAxisLineColorProperty); }
            set { SetValue(ZAxisLineColorProperty, value); }
        } 
        #endregion

        #endregion

        #region 变量

        private Vector3D lengthDirection;
        private Vector3D widthDirection; 

        #endregion

        #region 构造、析构

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public GridLinesVisual3D()
        {
            CreateGrid();
        }

        #endregion

        private static void GeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((GridLinesVisual3D)d).OnGeometryChanged();
        }

        private void OnGeometryChanged()
        {
            foreach (LinesVisual3D lines in Children)
                lines.IsRendering = false;

            Children.Clear();
            CreateGrid();
        }

        protected void CreateGrid()
        {

            var majorLinePoints = new Point3DCollection();
            var minorLinePoints = new Point3DCollection();
            var xLinePoints = new Point3DCollection();
            var yLinePoints = new Point3DCollection();

            lengthDirection = LengthDirection;
            lengthDirection.Normalize();
            widthDirection = Vector3D.CrossProduct(Normal, lengthDirection);
            widthDirection.Normalize();

            var mesh = new MeshBuilder(true, false);
            double minX = -Width / 2;
            double minY = -Length / 2;
            double maxX = Width / 2;
            double maxY = Length / 2;

            double x = minX;
            double eps = MinorDistance / 10;
            while (x <= maxX + eps)
            {
                var pc = IsMultipleOf(x, MajorDistance) ? majorLinePoints : minorLinePoints;
                if (x == 0)
                {
                    AddLine(pc, GetPoint(x, minY), GetPoint(x, 0));
                    AddLine(yLinePoints, GetPoint(x, 0), GetPoint(x, maxY));
                }
                else
                {
                    AddLine(pc, GetPoint(x, minY), GetPoint(x, maxY));
                }
                x += MinorDistance;
            }

            double y = minY;
            while (y <= maxY + eps)
            {
                var pc = IsMultipleOf(y, MajorDistance) ? majorLinePoints : minorLinePoints;
                if (y == 0)
                {
                    AddLine(pc, GetPoint(minX, y), GetPoint(0, y));
                    AddLine(xLinePoints, GetPoint(0, y), GetPoint(maxX, y));

                }
                else
                {
                    AddLine(pc, GetPoint(minX, y), GetPoint(maxY, y));
                }
                y += MinorDistance;
            }

            var majorLines = new LinesVisual3D
            {
                Color = this.MajorLineColor,
                Thickness = this.MajorLineThickness,
                Points = majorLinePoints,
                IsRendering = true
            };

            var minorLines = new LinesVisual3D
            {
                Color = this.MinorLineColor,
                Thickness = this.MinorLineThickness,
                Points = minorLinePoints,
                IsRendering = true
            };
            var xLines = new LinesVisual3D
            {
                Color = this.YAxisLineColor,
                Thickness = this.MajorLineThickness,
                Points = xLinePoints,
                IsRendering = true
            };
            var yLines = new LinesVisual3D
            {
                Color = this.XAxisLineColor,
                Thickness = this.MajorLineThickness,
                Points = yLinePoints,
                IsRendering = true
            };
            Children.Add(majorLines);
            Children.Add(minorLines);
            Children.Add(xLines);
            Children.Add(yLines);
        }

        private void AddLine(Point3DCollection pc, Point3D p0, Point3D p1, int divisions = 10)
        {
            var v = p1 - p0;
            for (int i = 0; i < divisions; i++)
            {
                pc.Add(p0 + v * (double)i / divisions);
                pc.Add(p0 + v * (double)(i + 1) / divisions);
            }
        }

        private static bool IsMultipleOf(double y, double d)
        {
            double y2 = d * (int)(y / d);
            return Math.Abs(y - y2) < 1e-3;
        }

        private Point3D GetPoint(double x, double y)
        {
            return Center + widthDirection * x + lengthDirection * y;
        }
    }
}
